home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga Plus 2000 #5
/
Amiga Plus CD - 2000 - No. 5.iso
/
Tools
/
Dev
/
fpc
/
demos
/
bezier.pas
< prev
next >
Wrap
Pascal/Delphi Source File
|
2000-01-01
|
7KB
|
252 lines
Program Bezier;
{
This program draws Bezier curves using the degree elevation
method. For large numbers of points (more than 10, for
example) this is faster than the recursive way.
}
{
History:
Changed the source to use 2.0+.
Looks a lot better.
Added CloseWindowSafely.
Made the window dynamic, it will
adjust the size after the screen size.
9 May 1998.
Translated the source to fpc.
20 Aug 1998.
Changed to use TAGS and pas2c.
31 Oct 1998.
nils.sjoholm@mailbox.swipnet.se
}
uses exec, intuition, graphics, utility,vartags, pastoc;
type
PointRec = packed Record
X, Y : Real;
end;
Const
w : pWindow = Nil;
s : pScreen = Nil;
{
This will make the new look for screen.
SA_Pens, Integer(pens)
}
pens : array [0..0] of integer = (not 0);
Var
rp : pRastPort;
PointCount : Word;
Points : Array [1..200] of PointRec;
LastX, LastY : Word;
Procedure CleanUpAndDie;
begin
if assigned(w) then CloseWindow(w);
if assigned(s) then CloseScreen(s);
if assigned(Gfxbase) then CloseLibrary(GfxBase);
Halt(0);
end;
Procedure DrawLine;
begin
Move(rp, Trunc(Points[PointCount].X), Trunc(Points[PointCount].Y));
Draw(rp, LastX, LastY);
end;
Procedure GetPoints;
var
LastSeconds,
LastMicros : Longint;
IM : pIntuiMessage;
StoreMsg : tIntuiMessage;
Leave : Boolean;
OutOfBounds : Boolean;
BorderLeft, BorderRight,
BorderTop, BorderBottom : Word;
dummy : Boolean;
Procedure AddPoint;
begin
Inc(PointCount);
with Points[PointCount] do begin
X := Real(StoreMsg.MouseX);
Y := Real(StoreMsg.MouseY);
end;
with StoreMsg do begin
LastX := MouseX;
LastY := MouseY;
LastSeconds := Seconds;
LastMicros := Micros;
end;
SetAPen(rp, 2);
SetDrMd(rp, JAM1);
DrawEllipse(rp, LastX, LastY, 5, 3);
SetAPen(rp, 3);
SetDrMd(rp, COMPLEMENT);
DrawLine;
end;
Function CheckForExit : Boolean;
{ This function determines whether the user wanted to stop
entering points. I added the position tests because my
doubleclick time is too long, and I was too lazy to dig
out Preferences to change it. }
begin
with StoreMsg do
CheckForExit := DoubleClick(LastSeconds, LastMicros,
Seconds, Micros) and
(Abs(MouseX - Trunc(Points[PointCount].X)) < 5) and
(Abs(MouseY - TRunc(Points[PointCount].Y)) < 3);
end;
Procedure ClearIt;
{ This just clears the screen when you enter your first point }
begin
SetDrMd(rp, JAM1);
SetAPen(rp, 0);
RectFill(rp, BorderLeft, BorderTop,
BorderRight, BorderBottom);
SetDrMd(rp, COMPLEMENT);
SetAPen(rp, 3);
end;
begin
dummy := ModifyIDCMP(w, IDCMP_CLOSEWINDOW or IDCMP_MOUSEBUTTONS or
IDCMP_MOUSEMOVE);
SetDrMd(rp, COMPLEMENT);
PointCount := 0;
Leave := False;
OutOfBounds := False;
BorderLeft := w^.BorderLeft;
BorderRight := (w^.Width - w^.BorderRight) -1;
BorderTop := w^.BorderTop;
BorderBottom := (w^.Height - w^.BorderBottom) -1;
repeat
IM := pIntuiMessage(WaitPort(w^.UserPort));
IM := pIntuiMessage(GetMsg(w^.UserPort));
StoreMsg := IM^;
ReplyMsg(pMessage(IM));
case StoreMsg.IClass of
IDCMP_MOUSEMOVE : if PointCount > 0 then begin
if not OutOfBounds then
DrawLine;
LastX := StoreMsg.MouseX;
LastY := StoreMsg.MouseY;
if (LastX > BorderLeft) and
(LastX < BorderRight) and
(LastY > BorderTop) and
(LastY < BorderBottom) then begin
DrawLine;
OutOfBounds := False;
end else
OutOfBounds := True;
end;
IDCMP_MOUSEBUTTONS : if StoreMsg.Code = SELECTUP then begin
if PointCount > 0 then
Leave := CheckForExit
else
ClearIt;
if (not Leave) and (not OutOfBounds) then
AddPoint;
end;
IDCMP_CLOSEWINDOW : CleanUpAndDie;
end;
until Leave or (PointCount > 50);
if not Leave then
DrawLine;
dummy := ModifyIDCMP(w, IDCMP_CLOSEWINDOW);
SetDrMd(rp, JAM1);
SetAPen(rp, 1);
end;
Procedure Elevate;
var
t, tprime,
RealPoints : Real;
i : Integer;
begin
Inc(PointCount);
RealPoints := Real(PointCount);
Points[PointCount] := Points[Pred(PointCount)];
for i := Pred(PointCount) downto 2 do
with Points[i] do begin
t := Real(i) / RealPoints;
tprime := 1.0 - t;
X := t * Points[Pred(i)].X + tprime * X;
Y := t * Points[Pred(i)].Y + tprime * Y;
end;
end;
Procedure DrawCurve;
var
i : Integer;
begin
Move(rp, Trunc(Points[1].X), Trunc(Points[1].Y));
for i := 2 to PointCount do
Draw(rp, Round(Points[i].X), Round(Points[i].Y));
end;
Procedure DrawBezier;
begin
SetAPen(rp, 2);
while PointCount < 100 do begin
Elevate;
DrawCurve;
if GetMsg(w^.UserPort) <> Nil then
CleanUpAndDie;
end;
SetAPen(rp, 1);
DrawCurve;
end;
begin
GfxBase := OpenLibrary(GRAPHICSNAME,37);
s := OpenScreenTagList(nil, TAGS(SA_Pens, Long(@pens),
SA_Depth, 2,
SA_DisplayID, HIRES_KEY,
SA_Title, Longstr('Simple Bezier Curves'),
TAG_END));
if s = NIL then CleanUpAndDie;
w := OpenWindowTagList(nil, TAGS(
WA_IDCMP, IDCMP_CLOSEWINDOW,
WA_Left, 0,
WA_Top, s^.BarHeight +1,
WA_Width, s^.Width,
WA_Height, s^.Height - (s^.BarHeight + 1),
WA_DepthGadget, ltrue,
WA_DragBar, ltrue,
WA_CloseGadget, ltrue,
WA_ReportMouse, ltrue,
WA_SmartRefresh, ltrue,
WA_Activate, ltrue,
WA_Title, longstr('Close the Window to Quit'),
WA_CustomScreen, long(s),
TAG_END));
IF w=NIL THEN CleanUpAndDie;
rp := w^.RPort;
Move(rp, 252, 30);
Text(rp, pas2c('Enter points by pressing the left mouse button'), 46);
Move(rp, 252, 40);
Text(rp, pas2c('Double click on the last point to begin drawing'), 47);
repeat
GetPoints; { Both these routines will quit if }
DrawBezier; { the window is closed. }
until False;
CleanUpAndDie;
end.